package main

import "fmt"
import "rdf"
// import "uri"
import "strconv"
import "regexp"
import "ntriples"
import "os"

func addToGraph( g *rdf.Graph ) func(*rdf.Triple) {
  return func (t *rdf.Triple) { g.Add( t ) }
}

func main() {
  fmt.Println( "welcome to the machine." )
  g := read( os.Args[1] )
  q := queryFromString( os.Args[2] )
  answers := q.run( g )
  for _, v := range answers { fmt.Println( v ) }
  // predicate := MatchURI{"http://www.electric-hedgehog.net/RDF/vocab/games/game#title"}
  // subject := MatchURI{"http://www.electric-hedgehog.net/RDF/vocab/games/game#Ivanhoe"}
  // g.Find( subject, graph.ANY, graph.ANY ).App( func (t *triple.Type) { fmt.Printf( "|> %v\n", t ) } )
  // ans := g.SelectSP( node.New("<http://www.electric-hedgehog.net/RDF/vocab/games/game#Ivanhoe>"), node.New("<http://www.electric-hedgehog.net/RDF/vocab/games/game#title>") )
  // fmt.Printf( ">> ans = %v\n", ans )
}

type MatchURI struct {spelling string}

func (m MatchURI) AsNode() *rdf.Node { return nil }

func (m MatchURI) CanMatch(n *rdf.Node) bool { 
  answer := n.String() == m.spelling
  return answer 
  }

func read( filename string ) *rdf.Graph {
  result := rdf.NewGraph()
  ntriples.ReadWith( filename, addToGraph( result ) )
  return result
}

type Query struct {
  patterns []*rdf.MatchTriple
}

func (q *Query) run( g *rdf.Graph ) []*Answer {
  g.FindAll( q.patterns, pt )
  return []*Answer{}
}

func pt( x rdf.Binding) { fmt.Println( x ) }

type Answer struct {

}

var pattern = regexp.MustCompile( "&|[(]|[)]|;|\\[|\\]|,|\\.|[a-z]+:[a-z]+|<[a-z]+:[^ ()[\\]>]+>|[?][\\-a-zA-Z_]+|\"[^\"]*\"(@[\\-a-z]+|\\^\\^XXX)?" )

func queryFromString( queryString string ) *Query {
  fmt.Printf( ">> pattern is %v\n", pattern )
  tokens := pattern.AllMatchesString( queryString, 0 )  
  fmt.Printf( ">> in queryFromString %v with tokens %v\n", queryString, tokens )
  pt := parse( tokens )
  result := &Query{pt}
  fmt.Printf( ">> []likeTriple %v\n", pt )
  return result
}

type Tokens struct {current string; rest []string}

func (t Tokens) at( s string ) bool { return t.current == s }

func (t *Tokens) advance() string {
  result := t.current
  if len(t.rest) == 0 { t.current = "" } else { t.current = t.rest[0]; t.rest = t.rest[1:] }
  return result
}

func (t *Tokens) breakUnless( x string ) bool {
  if t.current == x { t.advance(); return false }
  return true
}

func mkTokens( tokens []string ) *Tokens {
  return &Tokens{tokens[0], tokens[1:]}
}

type Set map[int]*rdf.MatchTriple

func (x Set) add( s, p, o string ) {
  fmt.Printf( "Set.add( %v, %v, %v )\n", s, p, o )
  t := rdf.NewMatchTriple( parseNode(s), parseNode(p), parseNode(o) )
  x[len(x)] = t
}

func parse( tokens []string ) []*rdf.MatchTriple {
  fmt.Printf( ">> parse: tokens = %v\n", tokens )
  set := make(Set)
  parseAll( mkTokens( tokens ), set )
  fmt.Printf( ">> parsed: set = %v\n", set )
  return toSlice(set)
}

func toSlice( s Set ) []*rdf.MatchTriple {
  slice := make([]*rdf.MatchTriple, len(s))
  for k, v := range s { slice[k] = v }
  return slice
}

func parseAll( t *Tokens, set Set ) {
  for {
    sameSubject( t, set )
    if t.breakUnless("&") { break }
  }
}

func sameSubject( t *Tokens, set Set ) {
  S := anyNode( t, set )
  if canPredicate( t ) { predications( S, t, set ) }
}

func canPredicate( t *Tokens ) bool {
  return !t.at("") && !t.at("]") && !t.at(")") && !t.at("&")
}

func predications( S string, t *Tokens, s Set ) {
  for {
    predication( S, t, s )
    if t.breakUnless(";") { break }
  }
}

func anyNode( t *Tokens, s Set ) string {
  switch t.current {
    case "[":
      S := makeBNode()
      t.advance()
      predications( S, t, s )
      if t.at("]") { t.advance() } else { fmt.Printf( "OOPS\n" ) }
      return S    
  }
  return t.advance()
}

var count = 1000

func makeBNode() string {
  count += 1
  return "?X" + strconv.Itoa(count)
}

func predication( S string, t *Tokens, s Set ) {
  P := t.advance()
  for {
    s.add( S, P, anyNode( t, s ) )
    if t.breakUnless(",") { break }
  }
}

/*
  Grammar:
  
  query = tripleish (& tripleish)*
  tripleish = nodeish nodeish nodeish
  nodeish = <stuff> | "stuff" | ?name
  
*/
func old_parse( tokens []string ) []*rdf.MatchTriple {
  result := make([]*rdf.MatchTriple, len(tokens), len(tokens))
  count, i := 0, 0
  for i < len(tokens) {
    s := parseNode( tokens[i] )
    p := parseNode( tokens[i + 1] )
    o := parseNode( tokens[i + 2] )
    i += 3
    if i < len(tokens) && tokens[i] == "&" { i += 1 }
    result[count] = rdf.NewMatchTriple( s, p, o )
    count += 1
  }
  result = result[0:count]
  return result
}

func parseNode( spelling string ) rdf.Matcher {
  if spelling[0] == '?' { return variable{spelling} }
  return rdf.NewNode( spelling )
}

type variable struct {spelling string}

func (v variable) AsNode() *rdf.Node { return nil }

func (v variable) Match( n *rdf.Node ) bool { return true }

func (v variable) AsVar() string { return v.spelling }

/*
fmt.Println( "all that glitters is not sunshine." )
  fmt.Printf( ">> %v\n", node.NewURI( uri.New( "broken" ) ) )
  fmt.Printf( ">> %v\n", node.NewURI( uri.New( "eh:/example" ) ) )
  g := graph.New()
  ntriples.ReadWith( "/tmp/data.nt", addToGraph( g ) )
  ans := g.With( uri.New( "http://www.w3.org/1999/02/22-rdf-syntax-ns#type" ) ).ToSet()
  fmt.Println( ans )
*/
